#include "MI5.hh"


/// Count reachable enemies according to the rules if one ant of team is at loc
unsigned count_reachable_enemies(Map & map, uLocation sLoc, signed team)
{
    unsigned enemies = 0;

    for(unsigned i=1; i<map.attack_reach_map()[sLoc].size(); ++i)
    {
        uLocation cLoc = map.attack_reach_map()[sLoc][i];
        if(map.at(cLoc).ant != team) enemies++;
    }

    return enemies;
}


/// Count reachable enemies according to the rules for a given ant position
/// assume my team if empty position
unsigned count_reachable_enemies(Map & map, uLocation sLoc)
{
    signed team = map[sLoc].ant;
    if(team == -1) team = map.myteam;
    return count_reachable_enemies(map, sLoc, team);
}



/// evaluates if a position is expected to die
bool will_die(Map & map, uLocation sLoc, signed team)
{
  unsigned enemies = count_reachable_enemies(map, sLoc,team);
  bool die = false;
    
  for(unsigned i=1; !die and i<map.attack_reach_map()[sLoc].size(); ++i)
    {
      uLocation cLoc = map.attack_reach_map()[sLoc][i];
      if(map[cLoc].ant != -1 and map[cLoc].ant != team)
	die = count_reachable_enemies(map, cLoc) <= enemies;
    }
    return die;
}


#if 0


/// evaluates if a position is expected to die
bool will_die(Map & map, uLocation sLoc)
{
    signed team = map[sLoc].ant;
    if(team == -1) team = map.myteam;
    unsigned enemies = count_reachable_enemies(map, sLoc);
    bool die = false;
    
    for(unsigned i=1; !die and i<map.attack_reach_map()[sLoc].size(); ++i)
    {
        uLocation cLoc = map.attack_reach_map()[sLoc][i];
        if(map[cLoc].ant != -1 and map[cLoc].ant != team)
            die = count_reachable_enemies(map, cLoc) <= enemies;
    }
    return die;
}

/// evaluates if a position is expected to die
/// and count enemies killed (slower)
bool will_die(Map & map, uLocation sLoc, unsigned & killed)
{
    signed team = map[sLoc].ant;
    if(team == -1) team = map.myteam;
    unsigned enemies = count_reachable_enemies(map, sLoc);
    bool die = false;
    killed = 0;
    
    for(unsigned i=0; i!=map.attack_reach_map()[sLoc].size(); ++i)
    {
        uLocation cLoc = map.attack_reach_map()[sLoc][i];
        if(map[cLoc].ant != -1 and map[cLoc].ant != team)
		{
            die = count_reachable_enemies(map, cLoc) <= enemies;
            if(count_reachable_enemies(map, cLoc) == enemies) killed++;
		}
    }
    return die;
}



/// evaluates if a position is expected to die in the worst case
bool may_die(Map & map, uLocation sLoc)
{
  bool risky = false;
  
  for(int d=0; d<Map::TDIRECTIONS and not risky; d++)
    {
      uLocation cLoc = map.getLocation(sLoc, d);
      if(map[cLoc].ant == -1)
	risky = will_die(map, cLoc);
    }

  if(risky)
    bug << "Risky position at " << map.get_row(sLoc)
	<< "," << map.get_col(sLoc) << std::endl;
  
  return risky;
}

#endif



/// evaluates if a position is expected to die in the worst case
bool may_die(Map & map, uLocation sLoc, signed team)
{
  bool risky = false;
  
  for(int d=0; d<Map::TDIRECTIONS and not risky; d++)
    {
      uLocation cLoc = map.getLocation(sLoc, d);
      risky = will_die(map, cLoc, team);
    }

  /*  if(risky)
    bug << "Risky position at " << map.get_row(sLoc)
	<< "," << map.get_col(sLoc) << std::endl;
  */

  
  return risky;
}



/// evaluates if a position is expected to die in the worst case
bool may_die(Map & map, uLocation from, uLocation to, signed team)
{
  ASSERT(map[from].ant==team);
  if(map[to].ant == team) return false;

  map[from].ant = -1;

  bool risky = false;

  for(int d=0; d<Map::TDIRECTIONS and not risky; d++)
    {
      uLocation cLoc = map.getLocation(to, d);
      if(cLoc != from)
	risky = will_die(map, cLoc, team);
    }

  map[from].ant=team;

  /*  if(risky)
    bug << "Risky position at " << map.get_row(sLoc)
	<< "," << map.get_col(sLoc) << std::endl;
  */

  
  return risky;
}





/// estimate safety level of positions according to neighbourhood
/// rought estimation: only for path-finding
void estimate_rough_safety(Map & map)
{

    // compute zero step safety
    for(uLocation sLoc = 0; sLoc!=map.size(); ++sLoc)
    {
        if(!map[sLoc].isWater and map[sLoc].isVisible)
        {
	  map[sLoc].isSafe = not will_die(map, sLoc,0);
        }
    }
}




/// Used at the end of turn to update hive estimate
/// Hive estimate is usefull to protect Hills: 
/// unlock the door at need allow to spawn new ants
bool this_food_is_for_me(Map & map, uLocation fLoc)
{
	unsigned enemies = 0;
	unsigned friends = 0;
	
	for(std::vector<uLocation>::const_iterator it_loc=map.food_reach_map()[fLoc].begin();
        it_loc!=map.food_reach_map()[fLoc].end(); ++it_loc)
	{
        uLocation cLoc = *it_loc;
	if(map.at(cLoc).ant != -1 and not will_die(map, cLoc,0))
		{
			if(map.at(cLoc).ant != 0 ) enemies++;
			else friends++;
		}
    }
	return friends>0 and enemies==0;
}


void update_hive_estimate_before_turn(State & state)
{
	TRACK();
	// WIP 
	if(state.turn<=1)
		{
			state.guards = 0;
		 	state.hive = state.myHills.size();
			state.ant_rate = 0.1;
		}
	unsigned ants_at_hill = 0;
	for(std::vector<uLocation>::const_iterator it_hill=state.myHills.begin();
	it_hill!=state.myHills.end(); ++it_hill)
	{
		if(state.grid()[*it_hill].ant == 0) ants_at_hill++;
	}
	state.ant_rate = 0.5 * state.ant_rate + 
	  0.5 * static_cast<double>((signed)state.guards - (signed)ants_at_hill);

	state.hive = state.hive + state.guards - ants_at_hill;  
	bug << "hive estimate: " << state.hive
	    << " ant rate: " << state.ant_rate << std::endl;

}


void update_hive_estimate_after_turn(State & state)
{
	for(std::vector<uLocation>::const_iterator it_food=state.food.begin();
        it_food!=state.food.end(); ++it_food)
		{
			if(this_food_is_for_me(state.grid(), *it_food))
				state.hive++;
		}
		
	state.guards = 0;	
	for(std::vector<uLocation>::const_iterator it_hill=state.myHills.begin();
		it_hill!=state.myHills.end(); ++it_hill)
		{
			if(state.grid()[*it_hill].ant == 0) state.guards++;
		}
		
}

void update_map_knowledge(State & state, cache_state_info_t & memory)
{
    std::set<uLocation>::iterator it=memory.enemyHills.begin();
    //erase out of date information
    while(it!=memory.enemyHills.end())
    {
        std::set<uLocation>::iterator next=it;
        next++;
        if(state.location(*it).isVisible)
            if(! state.location(*it).isHill || (state.location(*it).hillPlayer == 0))
                memory.enemyHills.erase(it);
        it=next;
    }
    //update places we can see now
    memory.enemyHills.insert(state.enemyHills.begin(),state.enemyHills.end());
    //mix places from sight and memory
    state.enemyHills.clear();
    state.enemyHills.insert(state.enemyHills.begin(),memory.enemyHills.begin(),memory.enemyHills.end());

    it=memory.food.begin();
    //erase out of date information
    while(it!=memory.food.end())
    {
        std::set<uLocation>::iterator next=it;
        next++;
        if(state.location(*it).isVisible)
            if(! state.location(*it).isFood)
                memory.food.erase(it);
        it=next;
    }
    //update places we can see now
    memory.food.insert(state.food.begin(),state.food.end());
    //mix places from sight and memory
    state.food.clear();
    state.food.insert(state.food.begin(),memory.food.begin(),memory.food.end());
}





